// bdldfp_uint128.h                                                   -*-C++-*-
#ifndef INCLUDED_BDLDFP_UINT128
#define INCLUDED_BDLDFP_UINT128

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a representation of a 128-bit `int` for bitwise operations.
//
//@CLASSES:
//  bdldfp::Uint128: A representation of a 128-bit unsigned integer
//
//@SEE_ALSO: bsl::bitset
//
//@DESCRIPTION: This component provides a value-semantic type,
// `bdldfp::Uint128`, that is used to represent a 128-bit unsigned integer
// having host-native byte order.  This component also provides a set of useful
// bitwise manipulation operators for this type.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Representing a 128 bit pattern for IPv6
/// - - - - - - - - - - - - - - - - - - - - - - - - -
// Starting in 1996, the world's TCP/IP infrastructure started the move to a
// 128-bit addressing scheme, IPv6.  IPv4 had a useful quality in that it could
// be represented by a single 32-bit machine word for internal routing
// purposes.  With IPv6, the need arises to manipulate 128-bit values
// representing IPv6 addresses for similar routing purposes.
//
// Suppose we need to write a function that needs to take 128-bit addresses
// indicating downstream routers in our network.
//
// First, we forward declare the "addRouter" function that takes an IPv6
// address indicating a router, and an IPv6 network address (a partial IP
// address, with trailing 0's) that the router covers:
// ```
// void addRouter(bdldfp::Uint128 router, bdldfp::Uint128 network);
// ```
// Now we create a function that loads a set of networks and routers:
// ```
// void setupNetwork()
// {
//     bdldfp::Uint128 network;
//     network.setHigh(0x4242000042420000L);
//     network.setLow(0x0);
//
//     bdldfp::Uint128 router(0x000012345678ABCDL, 0xDCBA000087654321L);
// ```
// Finally we invoke addRouter, on our network and router.
// ```
//     addRouter(router, network);
// }
// ```
// Notice that Uint128 values can be created from high/low pairs.
//
///Example 2: Checking a 128-bit IPv6 Network Mask
///- - - - - - - - - - - - - - - - - - - - - - - -
// In networks, checking a network mask is a fundamental operation.  A 128-bit
// mask can be used to indicate if an address is in a network, and where in the
// network an address is.
//
// Suppose we need to decide if an address is within a network, and extract the
// sub-address from the IPv6 address.
//
// First, we define a function that checks a passed IPv6 address and indicates
// the sub-address, and network membership:
// ```
// bool checkNetworkAddress(bdldfp::Uint128 *subAddress,
//                          bdldfp::Uint128  network,
//                          int              maskLength
//                          bdldfp::Uint128  address)
// {
// ```
// Then, we compute a net mask for the specified `maskLength`:
// ```
//     bdldfp::Uint128 netMask;
//     for (int i = 0; i < maskLength; ++i) {
//         netMask |= 1;
//         if (i != maskLength - 1) {
//             netMask <<= 1;
//         }
//     }
// ```
// Notice that it is possible to shift `Uint128` values as if they were a
// native type.  Meaning that it is possible to shift a `Uint128` by 64-bits,
// or more, in single operation, because `Uint128` functions just like a
// built-in 128-bit integer type would.
//
// Next, we calculate whether the passed address is within the network:
// ```
//     bool inNetwork = network == (address & ~netMask);
// ```
// Then, we compute the subAddress, if the address is in the network:
// ```
//     if (inNetwork) {
//         *subAddress = address & netMask;
//     }
// ```
// Now, we return whether the address is in the network, and close the
// function:
// ```
//     return inNetwork
// }
// ```
// Finally, we call `checkNetworkAddress` on a test network and address:
// ```
// bdldfp::Uint128 subAddress;
// assert(checkNetworkAddress(
//             &subAddress,
//             bdldfp::Uint128(0xABCD424200001234L,0x22FF12345678L),
//             bdldfp::Uint128(0xABCD424200001234L,0x22FF00000000L),
//             32); // The network has a 32-bit internal address mask.
// assert(subAddress == 0x12345678L);
// ```
// Notice that primitive 64-bit words can be promoted to 128-bit addresses.

#include <bsls_types.h>
#include <bsls_platform.h>

namespace BloombergLP {
namespace bdldfp {

                             // =============
                             // class Uint128
                             // =============

/// This value-semantic type represents a 128-bit integer, with host machine
/// byte order.
class Uint128 {

  private:
    // DATA

    // The high and low values are laid out in an architecture dependent
    // fashion to facilitate a native-endian layout.  This class is expected to
    // have standard layout.

    #ifdef BSLS_PLATFORM_IS_BIG_ENDIAN
    bsls::Types::Uint64 d_high;
    bsls::Types::Uint64 d_low;
    #elif defined(BSLS_PLATFORM_IS_LITTLE_ENDIAN)
    bsls::Types::Uint64 d_low;
    bsls::Types::Uint64 d_high;
    #else
    #error Only big or little endian is supported.
    #endif

  public:
    // CREATORS

    /// Create an Uint128 object having the value `0`
    Uint128();

    /// Create an `Uint128` object having the 128-bit integer bit pattern of
    /// the value of the specified `initialValue`.
    Uint128(bsls::Types::Uint64 initialValue);                      // IMPLICIT

    /// Create an `Uint128` object having the 128-bit pattern specified by
    /// `initialHigh..initialLow`
    Uint128(bsls::Types::Uint64 initialHigh, bsls::Types::Uint64 initialLow);

    //! ~Uint128() = default;
        // Destroy this object.

    // MANIPULATORS
    //! Uint128& operator=(const Uint128& rhs) = default;
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    /// Set the value of this object to the value of a the bitwise or
    /// between this 128 bit integer and the specified `rhs` value, and
    /// return a reference providing mofifiable access to this object.
    Uint128& operator|=(const Uint128& rhs);

    /// Set the value of this object to the value of a the bitwise and
    /// between this 128 bit integer and the specified `rhs` value, and
    /// return a reference providing mofifiable access to this object.
    Uint128& operator&=(const Uint128& rhs);

    /// Set the value of this object to the value of a the bitwise xor
    /// between this 128 bit integer and the specified `rhs` value, and
    /// return a reference providing mofifiable access to this object.
    Uint128& operator^=(const Uint128& rhs);

    /// Set the value of this object to the value of a bitwise right shift
    /// of this 128 bit integer shifted by the specified `rhs` value, and
    /// return a reference providing mofifiable access to this object.  The
    /// behavior is undefined unless `0 <= rhs < 128`.
    Uint128& operator>>=(int rhs);

    /// Set the value of this object to the value of a bitwise left shift of
    /// this 128 bit integer shifted by the specified `rhs` value, and
    /// return a reference providing mofifiable access to this object.  The
    /// behavior is undefined unless `0 <= rhs < 128`.
    Uint128& operator<<=(int rhs);

    /// Set the high order bits of this integer to the specified `value`.
    void setHigh(bsls::Types::Uint64 value);

    /// Set the low order bits of this integer to the specified `value`.
    void setLow(bsls::Types::Uint64 value);

    // ACCESSORS

    /// Return the high order bits of this integer.
    bsls::Types::Uint64 high() const;

    /// Return the low order bits of this integer.
    bsls::Types::Uint64 low() const;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and the specified `rhs` objects
/// have the same value, and `false` otherwise.  Two `Uint128` objects have
/// the same value if both of their `low` and `high` attributes are the
/// same.
bool operator==(const Uint128& lhs, const Uint128& rhs);

/// Return `true` if the specified `lhs` and the specified `rhs` objects do
/// not have the same value, and `false` otherwise.  Two `Uint128` objects
/// do not have the same value if either of their `low` and `high`
/// attributes are not the same.
bool operator!=(const Uint128& lhs, const Uint128& rhs);

/// Return an Uint128 object having the value of a the bitwise or between
/// the specified `lhs` and the specified `rhs` value.
Uint128 operator|(Uint128 lhs, const Uint128& rhs);

/// Return an Uint128 object having the value of a the bitwise and between
/// the specified `lhs` and the specified `rhs` value.
Uint128 operator&(Uint128 lhs, const Uint128& rhs);

/// Return an Uint128 object having the value of a the bitwise xor between
/// the specified `lhs` and the specified `rhs` value.
Uint128 operator^(Uint128 lhs, const Uint128& rhs);

/// Return an `Uint128` value equal to the value of a bitwise left shift of
/// the specified `lhs` 128-bit integer shifted by the specified `rhs`
/// value.  The behavior is undefined unless `0 <= rhs < 128`.
Uint128 operator<<(Uint128 lhs, int rhs);

/// Return an `Uint128` value equal to the value of a bitwise right shift of
/// the specified `lhs` 128-bit integer shifted by the specified `rhs`
/// value.  The behavior is undefined unless `0 <= rhs < 128`.
Uint128 operator>>(Uint128 lhs, int rhs);

/// Return an `Uint128` value equal to the bitwise ones compliment of the
/// specified `value`.
Uint128 operator ~(Uint128 value);

// ============================================================================
//                      INLINE FUNCTION DEFINITIONS
// ============================================================================

                        // -------------
                        // class Uint128
                        // -------------

// CREATORS
inline
Uint128::Uint128()
{
    d_low  = 0;
    d_high = 0;
}

inline
Uint128::Uint128(bsls::Types::Uint64 initialValue)
{
    d_high = 0;
    d_low  = initialValue;
}

inline
Uint128::Uint128(bsls::Types::Uint64 initialHigh,
                 bsls::Types::Uint64 initialLow)
{
    d_high = initialHigh;
    d_low  = initialLow;
}

// MANIPULATORS
inline
Uint128& Uint128::operator|=(const Uint128& rhs)
{
    d_high |= rhs.d_high;
    d_low  |= rhs.d_low;

    return *this;
}

inline
Uint128& Uint128::operator&=(const Uint128& rhs)
{
    d_high &= rhs.d_high;
    d_low  &= rhs.d_low;

    return *this;
}

inline
Uint128& Uint128::operator^=(const Uint128& rhs)
{
    d_high ^= rhs.d_high;
    d_low  ^= rhs.d_low;

    return *this;
}

inline
Uint128& Uint128::operator>>=(int rhs)
{
    if (rhs == 0) {
        return *this;                                                 // RETURN
    }

    if (rhs < 64) {
        d_low  >>= rhs;
        d_low  |= d_high << (64 - rhs);
        d_high >>= rhs;
    }
    else {
        d_low  = d_high >> (rhs - 64);
        d_high = 0;
    }

    return *this;
}

inline
Uint128& Uint128::operator<<=(int rhs)
{
    if (rhs == 0) {
        return *this;                                                 // RETURN
    }

    if (rhs < 64) {
        d_high <<= rhs;
        d_high |= d_low >> (64 - rhs);
        d_low  <<= rhs;
    }
    else {
        d_high = d_low << (rhs - 64);
        d_low  = 0;
    }

    return *this;
}

inline
void Uint128::setHigh(bsls::Types::Uint64 value)
{
    d_high = value;
}

inline
void Uint128::setLow(bsls::Types::Uint64 value)
{
    d_low = value;
}

// ACCESSORS
inline
bsls::Types::Uint64 Uint128::high() const
{
    return d_high;
}

inline
bsls::Types::Uint64 Uint128::low() const
{
    return d_low;
}


}  // close package namespace

// FREE OPERATORS
inline
bool bdldfp::operator==(const bdldfp::Uint128& lhs,
                        const bdldfp::Uint128& rhs)
{
    return lhs.high() == rhs.high() && lhs.low() == rhs.low();
}

inline
bool bdldfp::operator!=(const bdldfp::Uint128& lhs,
                        const bdldfp::Uint128& rhs)
{
    return lhs.high() != rhs.high() || lhs.low() != rhs.low();
}

inline
bdldfp::Uint128 bdldfp::operator|(      bdldfp::Uint128  lhs,
                                  const bdldfp::Uint128& rhs)
{
    return lhs |= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator&(      bdldfp::Uint128  lhs,
                                  const bdldfp::Uint128& rhs)
{
    return lhs &= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator^(      bdldfp::Uint128  lhs,
                                  const bdldfp::Uint128& rhs )
{
    return lhs ^= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator<<(bdldfp::Uint128 lhs, int rhs)
{
    return lhs <<= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator>>(bdldfp::Uint128 lhs, int rhs)
{
    return lhs >>= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator~(bdldfp::Uint128 value)
{
    value.setHigh(~value.high());
    value.setLow( ~value.low());

    return value;
}

}  // close enterprise namespace
#endif

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
